﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Brain.Editor
{
    public class PickingManager3D_Bounding : IPickingManager3D
    {
        public IWorldObject SelectedWorldObject { get; set; }

        public bool Manipulating { get; set; }

        public ManipulationMode3D Mode { get; set; }

        public bool ShouldUpdate { get; set; }

        public WorldObject XMani;
        public WorldObject YMani;
        public WorldObject ZMani;

        public event SetWorldObject OnSelected;

        public float ScaleBefore;
        public Vector3 UnitAxis;

        public PickingManager3D_Bounding()
        {
            XMani = new WorldObject();
            XMani.Scale = new Vector3(5, 1, 1);
            XMani.BoundingBox = new BoundingBox(Vector3.Zero, XMani.Scale);
            YMani = new WorldObject();
            YMani.Scale = new Vector3(1, 5, 1);
            YMani.BoundingBox = new BoundingBox(Vector3.Zero, YMani.Scale);
            ZMani = new WorldObject();
            ZMani.Scale = new Vector3(1, 1, 5);
            ZMani.BoundingBox = new BoundingBox(Vector3.Zero, ZMani.Scale);

            ShouldUpdate = true;
            ShouldDraw = true;
        }

        bool added = false;
        public void Update()
        {
            if (Engine.Instance.RenderManager3D == null)
            {
                return;
            }

            if (!added && SelectedWorldObject != null)
            {
                XMani.Rendering = WorldObjectRendering.EditorVisible;
                YMani.Rendering = WorldObjectRendering.EditorVisible;
                ZMani.Rendering = WorldObjectRendering.EditorVisible;
            }
            XMani.Update();
            YMani.Update();
            ZMani.Update();

            XMani.BoundingBox = new BoundingBox(XMani.Position, XMani.Position + XMani.Scale);
            YMani.BoundingBox = new BoundingBox(YMani.Position, YMani.Position + YMani.Scale);
            ZMani.BoundingBox = new BoundingBox(ZMani.Position, ZMani.Position + ZMani.Scale);

            ICamera3D camera = Engine.Instance.CameraManager.Camera3D;
            GraphicsDevice device = Engine.Instance.GraphicsDevice;
            IWorldObject wo_Mani = null;
            if (!Manipulating && camera != null)
            {
                Vector2 mousePos = Engine.Instance.InputManager.GetMousePosition();
                int X = (int)mousePos.X;
                int Y = (int)mousePos.Y;
                Vector3 v;
                wo_Mani = Pick(X, Y, device.Viewport, camera, out v);
                if (Engine.Instance.InputManager.IsMouseLeftDown())
                {
                    if (wo_Mani != null &&
                        wo_Mani != this.XMani &&
                        wo_Mani != this.YMani &&
                        wo_Mani != this.ZMani &&
                        wo_Mani != this.SelectedWorldObject)
                    {
                        this.SelectedWorldObject = wo_Mani;
                        if (OnSelected != null)
                            OnSelected(wo_Mani);
                    }
                }
                UnitAxis = new Vector3(
                        wo_Mani == XMani ? 1 : 0,
                        wo_Mani == YMani ? 1 : 0,
                        wo_Mani == ZMani ? 1 : 0);
            }
            if (UnitAxis.Length() > 0)
            {
            }

            Vector3 Position = Vector3.Zero;
            float size = 30;
            float RelativeScale = 5;
            float RelativeScaleOverTwo = RelativeScale / 2f;
            if (this.SelectedWorldObject != null && camera != null)
            {
                Position = SelectedWorldObject.Position;

                if (!Manipulating)
                {
                    Vector3 viewPosition = camera.Position;

                    Matrix ScaleView = Matrix.CreateLookAt(Vector3.UnitZ *
                        Vector3.Distance(viewPosition, Position), Vector3.Zero, Vector3.Up);

                    Vector3 ProjStart = Engine.Instance.GraphicsDevice.Viewport.Project(
                        Vector3.Zero, camera.Projection, ScaleView, Matrix.Identity);
                    Vector3 ProjEnd = Engine.Instance.GraphicsDevice.Viewport.Project(
                        Vector3.UnitX, camera.Projection, ScaleView, Matrix.Identity);

                    RelativeScale = size / (ProjEnd - ProjStart).Length();
                    RelativeScale *= 2;
                    RelativeScaleOverTwo = RelativeScale / 2f;

                    ScaleBefore = RelativeScale;
                }
                else
                {
                    RelativeScale = ScaleBefore;
                    RelativeScaleOverTwo = ScaleBefore / 2f;
                }

                float relative = RelativeScaleOverTwo / 2f;
                this.XMani.Scale = new Vector3(relative * 5, relative, relative);
                this.YMani.Scale = new Vector3(relative, relative * 5, relative);
                this.ZMani.Scale = new Vector3(relative, relative, relative * 5);

                if (Engine.Instance.InputManager.IsMouseLeftDown() &&
                    camera != null)
                {
                    Manipulating = true;

                    if (UnitAxis != Vector3.Zero)
                    {
                        Vector3.TransformNormal(UnitAxis, SelectedWorldObject.WorldMatrix);

                        Matrix Translation = SelectedWorldObject.WorldMatrix;
                        Matrix Projection = camera.Projection;
                        Matrix View = camera.View;
                        Viewport Viewport = Engine.Instance.GraphicsDevice.Viewport;
                        Vector2 Delta = Engine.Instance.InputManager.GetMouseDifference();

                        Vector3 StartPosition = Viewport.Project(Vector3.Zero, Projection, View, Translation);
                        Vector3 EndPosition = Viewport.Project(UnitAxis, Projection, View, Translation);

                        Vector3 ScreenDirection = Vector3.Normalize(EndPosition - StartPosition);
                        EndPosition = StartPosition + (ScreenDirection * (Vector3.Dot(new Vector3(Delta, 0f), ScreenDirection)));

                        StartPosition = Viewport.Unproject(StartPosition, Projection, View, Translation);
                        EndPosition = Viewport.Unproject(EndPosition, Projection, View, Translation);

                        Vector3 Difference = EndPosition - StartPosition;

                        switch (Mode)
                        {
                            case ManipulationMode3D.Translation:
                                SelectedWorldObject.Position += Difference;
                                break;
                            case ManipulationMode3D.Scale:
                                SelectedWorldObject.Scale += Difference;
                                break;
                            case ManipulationMode3D.UniformScale:
                                SelectedWorldObject.Scale += new Vector3(Vector3Util.Average(Difference));
                                break;

                        }
                    }
                }
                else
                {
                    Manipulating = false;
                }
            }

            XMani.Draw();
            XMani.Position = Position + new Vector3(RelativeScaleOverTwo, 0, 0);
            YMani.Draw();
            YMani.Position = Position + new Vector3(0, RelativeScaleOverTwo, 0);
            ZMani.Draw();
            ZMani.Position = Position + new Vector3(0, 0, RelativeScaleOverTwo);
        }


        public IWorldObject Pick(int X, int Y, Viewport viewport, ICamera3D Camera, out Vector3 Axis)
        {
            Axis = Vector3.Zero;
            IRenderManager3D renderManager = Engine.Instance.RenderManager3D;

            Ray MouseRay = MakeMouseRay(X, Y, viewport, Camera);
            for (int i = 0; i < renderManager.Editor_Count(); i++)
            {
                IWorldObject wo = renderManager.Editor_GetWorldObject(i);

                float? box = MouseRay.Intersects(wo.BoundingBox);

                if (box != null)
                {
                    return wo;
                }

                //Matrix inverseTransform = Matrix.Invert(modelTransform);
                //MouseRay.Position = Vector3.Transform(ray.Position, inverseTransform);
                //MouseRay.Direction = Vector3.TransformNormal(ray.Direction, inverseTransform);
            }


            return null;
        }

        protected Ray MakeMouseRay(int X, int Y, Viewport viewport, ICamera3D camera)
        {
            Vector2 Position = new Vector2(X, Y);
            Vector3 nearSource = new Vector3(Position, 0f);
            Vector3 farSource = new Vector3(Position, 1f);

            Vector3 nearPoint = viewport.Unproject(nearSource,
                camera.Projection, camera.View, Matrix.Identity);

            Vector3 farPoint = viewport.Unproject(farSource,
                camera.Projection, camera.View, Matrix.Identity);

            Vector3 direction = farPoint - nearPoint;
            if (direction != Vector3.Zero)
            {
                direction.Normalize();
            }

            return new Ray(nearPoint, direction);
        }


        public bool ShouldDraw { get; set; }

        public void Draw()
        {
        }


        public void EndDraw()
        {
            throw new NotImplementedException();
        }


        public void EnableTerrainEditting()
        {
            throw new NotImplementedException();
        }


        public void SetTerrainEdittingStrength(float strength)
        {
            throw new NotImplementedException();
        }

        public void SetTerrainEdittingSize(float size)
        {
            throw new NotImplementedException();
        }
    }
}
